package com.gsbelarus.gedemin.lib.sync.protocol.etc;

import android.content.ContentValues;

import com.gsbelarus.gedemin.lib.sync.protocol.exception.AddressFaildException;
import com.gsbelarus.gedemin.lib.sync.protocol.exception.AuthFaildException;
import com.gsbelarus.gedemin.lib.sync.protocol.exception.IDFaildException;
import com.gsbelarus.gedemin.lib.sync.protocol.table.ParsHeaderTable;
import com.gsbelarus.gedemin.lib.sync.protocol.table.ParsTable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Анализатор данных
 */
final public class Parser {

    /**
     * Список команд
     */
    public static final int RALL = 0;
    public static final int INSERT = 1;
    public static final int UPDATE = 2;
    public static final int RESET = 3;
    public static final int DELETE = 4;
    public static final int VER = 5;

    private CSVReader csvCommands;
    private CSVReader csvHeader;

    public Parser() {
        csvCommands = new CSVReader(',', '"');
        csvHeader = new CSVReader('=');
    }

    /**
     * Метод для анализа заголовка блока.
     * Виды заголовка:
     * ======================
     * MAX_VER=123
     * GUID=123
     * ======================
     * NUM_VER=123
     * GUID=123
     * ======================
     * authentication failed
     * ======================
     * unknown serial number
     * ======================
     *
     * @param line List строк данных
     * @return
     * @throws IOException
     * @throws IndexOutOfBoundsException
     */
    public ParsHeaderTable parsingHeader(List<String> line) throws IOException, IndexOutOfBoundsException {
        ParsHeaderTable header = new ParsHeaderTable();
        if (line != null) {

            if (line.get(0).contains("unknown serial number"))
                throw  new IDFaildException();

            if (line.get(0).contains("authentication failed"))
                throw new AuthFaildException();

            if (line.get(0).contains("MAX_NUM=") || line.get(0).contains("NUM_VER="))
                header.setNum(Integer.parseInt(csvHeader.parseLine(line.get(0))[1]));
            else
                throw new AddressFaildException();

            if (line.get(1).contains("GUID=")) {
                header.setGuid(csvHeader.parseLine(line.get(1))[1]);

            } else
                throw new IOException("parsingHeader");

        } else throw new AddressFaildException();
        return header;
    }

    /**
     * Метод для анализа тела блока.
     * Виды команд:
     * I - вставка записи
     * U - обновление записи
     * R - удаление таблицы
     * D - удаление записей
     * RALL - удаление всех таблиц
     *
     * @param lines List строк загруженных данных
     * @return готовые для обновления БД данные
     * @throws IOException
     * @throws IndexOutOfBoundsException
     */
    public List<ParsTable> parsing(List<String> lines) throws IOException, IndexOutOfBoundsException {
        List<ParsTable> parsTables = new ArrayList<ParsTable>();
        for (String line : lines) {
            String command = csvCommands.parseLine(line)[0];

            if (command.equals("I"))
                parsTables.add(insertOrUpdate(line, INSERT));

            else if (command.equals("U"))
                parsTables.add(insertOrUpdate(line, UPDATE));

            else if (command.equals("R"))
                parsTables.add(reset(line));

            else if (command.equals("D"))
                parsTables.add(delete(line));

            else if (command.equals("RALL"))
                parsTables.add(new ParsTable(RALL));

            else if (line.contains("NUM_VER="))
                parsTables.add(updateVer(line));
        }
        return parsTables;
    }

    /**
     * пример приходящей строки:
     * 0     1        2        3         4          5          6         7     8
     * I,MB_ENTRY,154724352,ENTRYID,"154724352",ENTRYDATE,"2005-04-14",DEBIT,"60", ......
     */
    private ParsTable insertOrUpdate(String line, int command) throws IOException {
        ParsTable pars = new ParsTable();
        ContentValues cv = new ContentValues();

        String[] csvArray = csvCommands.parseLine(line);
        /** команда */
        pars.setCommand(command);
        /** имя таблицы */
        pars.setTable(csvArray[1]);
        /** UID записи */
        cv.put("ID", csvArray[2]);
        /** поле-значение таблицы */
        for (int i = 3; i < csvArray.length; i += 2) {
            cv.put(csvArray[i], csvArray[i + 1]);
        }
        pars.setContentValues(cv);

        return pars;
    }

    private ParsTable reset(String line) throws IOException {
        ParsTable pars = new ParsTable();
        pars.setCommand(RESET);
        pars.setTable(csvCommands.parseLine(line)[1]);
        return pars;
    }

    /**
     * пример приходящей строки:
     * 0    1         2         3         4
     * D,MB_ENTRY,154724352,154724353,154724354, .........
     */
    private ParsTable delete(String line) throws IOException {
        ParsTable pars = new ParsTable();
        String[] buf = csvCommands.parseLine(line);
        pars.setCommand(DELETE);
        pars.setTable(buf[1]);
        String[] values = new String[buf.length - 2];
        int j = 0;
        for (int i = 2; i < buf.length; i++) {
            values[j] = buf[i];
            j++;
        }
        pars.setValues(values);

        return pars;
    }

    private ParsTable updateVer(String line) throws IOException {
        ParsTable pars = new ParsTable();
        pars.setCommand(VER);
        pars.setValues(new String[]{csvHeader.parseLine(line)[1]});
        return pars;
    }
}
